package org.quickstart.client.view;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.ListBox;
import com.google.inject.Inject;
import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.place.Place;
import net.customware.gwt.presenter.client.place.PlaceChangedEvent;
import net.customware.gwt.presenter.client.place.PlaceRequest;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import net.customware.gwt.presenter.client.widget.WidgetPresenter;
import org.quickstart.api.action.GetContactsAction;
import org.quickstart.api.action.UpdateContactAction;
import org.quickstart.api.service.ContactsService;
import org.quickstart.api.service.ContactsServiceAsync;
import org.quickstart.client.event.ContactUpdatedEvent;
import org.quickstart.model.Contact;

import java.util.ArrayList;

/**
 * @author Yoav Aharoni
 */
public class PhoneBookPresenter extends WidgetPresenter<PhoneBookPresenter.Display> implements ContactUpdatedEvent.Handler {
	private static final Place PLACE = new Place(PhoneBookPresenter.class);
	private ContactMessages messages = GWT.create(ContactMessages.class);

	private final DispatchAsync dispatcher;

	@Inject
	public PhoneBookPresenter(Display display, EventBus eventBus, DispatchAsync dispatcher) {
		super(display, eventBus);
		this.dispatcher = dispatcher;
	}

	@Override
	public void refreshDisplay() {
	}

	@Override
	public void revealDisplay() {
		// Load contacts into ListBox using dispatch
		final ListBox contactListBox = display.getContactListBox();
		if (contactListBox.getItemCount() == 0) {
			// Load list using by dispatching GetContactsAction
			dispatcher.execute(new GetContactsAction(), new AsyncCallback<GetContactsAction.Result>() {
				@Override
				public void onFailure(Throwable caught) {
					Window.alert("Error loading contacts!");
				}

				@Override
				public void onSuccess(GetContactsAction.Result result) {
					final ArrayList<Contact> contacts = result.get();
					for (Contact contact : contacts) {
						final String item = messages.contactItemFormat(contact.getName(), contact.getPhone());
						contactListBox.addItem(item, contact.getName());
					}
				}
			});
		}
	}

	/**
	 * Fired when contact is updated.
	 */
	@Override
	public void onContactUpdate(ContactUpdatedEvent event) {
		// Update ListBox according
		final Contact contact = event.getContact();
		final int i = getContactIndex(event.getOldName());
		if (i >= 0) {
			final ListBox contactList = display.getContactListBox();
			contactList.setValue(i, contact.getName());
			contactList.setItemText(i, messages.contactItemFormat(contact.getName(), contact.getPhone()));
		}

		final String editLabelText = messages.contactEditFormat(contact.getName());
		display.getEditContactLabel().setText(editLabelText);
	}

	@Override
	public Place getPlace() {
		return PLACE;
	}

	@Override
	protected void onPlaceRequest(PlaceRequest placeRequest) {
		// Update ListBox selected item according to current place
		final String name = placeRequest.getParameter("contact", "");
		final int i = getContactIndex(name);
		if (i >= 0) {
			display.getContactListBox().setItemSelected(i, true);
		}
		updateContactFields();
	}

	@Override
	protected void onBind() {
		eventBus.addHandler(ContactUpdatedEvent.TYPE, this);

		// Add testRpc button click handler
		final ContactsServiceAsync service = GWT.create(ContactsService.class);
		final ListBox contactsListBox = display.getContactListBox();

		display.getTestRpcButton().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				// Load Contacts From RPC
				service.getContacts(new AsyncCallback<ArrayList<Contact>>() {
					@Override
					public void onFailure(Throwable caught) {
						Window.alert("Error loading contacts!");
					}

					@Override
					public void onSuccess(ArrayList<Contact> contacts) {
						contactsListBox.clear();
						for (Contact contact : contacts) {
							final String item = messages.contactItemFormat(contact.getName(), contact.getPhone());
							contactsListBox.addItem(item, contact.getName());
						}
					}
				});
			}
		});

		// Add testRpc button click handler
		display.getUpdateContactButton().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				// Update Contact
				final String name = display.getNameField().getValue();
				final String newPhone = display.getPhoneField().getValue();
				final Contact contact = new Contact(name, newPhone);
				updateContact(name, contact);
			}
		});

		// Add contactsListBox change handler
		contactsListBox.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				// Update Edit Fields
				updateContactFields();
				final String name = display.getEditContactLabel().getText();
				final PlaceRequest place = new PlaceRequest(getPlace()).with("contact", name);
				eventBus.fireEvent(new PlaceChangedEvent(place));
			}
		});
	}

	@Override
	protected void onUnbind() {
	}


	/**
	 * Get contact index by name.
	 *
	 * @param contactName contact name
	 * @return index in ListBox
	 */
	private int getContactIndex(String contactName) {
		final ListBox contactList = display.getContactListBox();
		for (int i = 0; i < contactList.getItemCount(); i++) {
			final String value = contactList.getValue(i);
			if (value.equals(contactName)) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Update Edit Contact Fields
	 */
	private void updateContactFields() {
		final ListBox contactListBox = display.getContactListBox();
		final int index = contactListBox.getSelectedIndex();
		if (index >= 0) {
			final String value = contactListBox.getValue(index);
			display.getEditContactLabel().setText(messages.contactEditFormat(value));
			display.getNameField().setValue(value);
		} else {
			display.getEditContactLabel().setText("Select Contact");
		}
	}

	private void updateContact(String oldName, Contact contact) {
		dispatcher.execute(new UpdateContactAction(oldName, contact), new AsyncCallback<UpdateContactAction.Result>() {
			@Override
			public void onFailure(Throwable caught) {
				Window.alert("Error loading contacts!");
			}

			@Override
			public void onSuccess(UpdateContactAction.Result result) {
				final Contact contact = result.getNew();
				eventBus.fireEvent(new ContactUpdatedEvent(result.getOld().getName(), contact));
			}
		});
	}

	public static interface Display extends WidgetDisplay {
		HasText getEditContactLabel();

		HasValue<String> getPhoneField();

		HasValue<String> getNameField();

		ListBox getContactListBox();

		HasClickHandlers getUpdateContactButton();

		HasClickHandlers getTestRpcButton();
	}
}
